#!/usr/bin/envpython
# -*-coding:UTF-8-*-
'''
@File    :   concurrency_helper.py
@Contact :   308711822@qq.com
@License :   (C) Copyright 2021-2225, Personal exclusive right.

@Modify Time      @Author    @Version    @Desciption
------------      -------    --------    -----------
2023/2/4 12:03   小钟同学      1.0         None
'''
import inspect
from typing import Any, Callable, TypeVar
from contextlib import asynccontextmanager
import functools
import asyncio

T = TypeVar("T")


def is_coroutine_callable(call: Callable[..., Any]) -> bool:
    '''
    判断是否是协程
    :param call:
    :return:
    '''
    if inspect.isroutine(call):
        return inspect.iscoroutinefunction(call)
    if inspect.isclass(call):
        return False
    call = getattr(call, "__call__", None)
    return inspect.iscoroutinefunction(call)


@asynccontextmanager
async def contextmanager_in_threadpool(cm: Any) -> Any:
    try:
        yield await run_in_threadpool(cm.__enter__)
    except Exception as e:
        ok = await run_in_threadpool(cm.__exit__, type(e), e, None)
        if not ok:
            raise e
    else:
        await run_in_threadpool(cm.__exit__, None, None, None)


async def run_in_threadpool(func: Callable[..., T], *args: Any, **kwargs: Any) -> T:
    '''
      elif is_coroutine_callable(call):
                solved = await call(**sub_values)
            else:
                solved = await run_in_threadpool(call, **sub_values)
    :param func: 要处理的函数
    :param args: 具体参数
    :param kwargs:
    :return:
    '''
    loop = asyncio.get_event_loop()
    func = functools.partial(func, **kwargs)
    return await loop.run_in_executor(None, func, *args)


import asyncio
from concurrent.futures import ThreadPoolExecutor


def run(coroutine_function, *args, **kwargs):
    if not asyncio.iscoroutinefunction(coroutine_function):
        raise TypeError('"coroutine_function" must be a coroutine_function.')
    try:
        coro = coroutine_function(*args, **kwargs)
        result = asyncio.run(coro)
        return result
    except:
        coro = coroutine_function(*args, **kwargs)
        with ThreadPoolExecutor() as executor:
            result = executor.submit(asyncio.run, coro).result()
            return result
